package com.platform.common.core.utils.poi;

import com.google.common.collect.Lists;
import com.platform.common.core.annotation.Excel;
import com.platform.common.core.annotation.Excels;
import com.platform.common.core.domain.export.ExportFieldDTO;
import com.platform.common.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 自定义excel导出
 * @Author: wcs
 * @Date: 2024-07-10 10:52
 **/
@Slf4j
public class CustomExcelUtils<T> extends ExcelUtil<T> {

    private final List<String> needExportFields;

    public CustomExcelUtils(Class<T> clazz, List<String> needExportFields) {
        super(clazz);
        this.needExportFields = needExportFields;
    }

    @Override
    public List<Object[]> getFields() {
        List<Object[]> fields = new ArrayList<>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields) {
            if (!ArrayUtils.contains(this.excludeFields, field.getName()) && needExportFields.contains(field.getName())) {
                // 单注解
                if (field.isAnnotationPresent(Excel.class)) {
                    Excel attr = field.getAnnotation(Excel.class);
                    if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == getType())) {
                        field.setAccessible(true);
                        fields.add(new Object[]{field, attr});
                    }
                    if (Collection.class.isAssignableFrom(field.getType())) {
                        Method subMethod = getSubMethod(field.getName(), clazz);
                        setSubMethod(subMethod);
                        ParameterizedType pt = (ParameterizedType) field.getGenericType();
                        Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                        List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
                        setSubFields(subFields);
                    }
                }
                // 多注解
                if (field.isAnnotationPresent(Excels.class)) {
                    Excels attrs = field.getAnnotation(Excels.class);
                    Excel[] excels = attrs.value();
                    for (Excel attr : excels) {
                        if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == getType())) {
                            field.setAccessible(true);
                            fields.add(new Object[]{field, attr});
                        }
                    }
                }
            }
        }
        return fields;
    }

    public static List<ExportFieldDTO> getExportObjectAllFields(Class<?> clazz) {
        List<Object[]> res = Lists.newArrayList();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Excel.class)) {
                Object[] re = new Object[]{field, field.getAnnotation(Excel.class)};
                res.add(re);
            }
        }
        res = res.stream().sorted(Comparator.comparing(object -> ((Excel) object[1]).sort())).collect(Collectors.toList());
        return res.stream().map(item -> {
            ExportFieldDTO exportFieldDTO = new ExportFieldDTO();
            exportFieldDTO.setFieldName(((Field) item[0]).getName());
            exportFieldDTO.setDescription(((Excel) item[1]).name());
            return exportFieldDTO;
        }).collect(Collectors.toList());
    }


    public Excel.Type getType() {

        try {
            Field field = ExcelUtil.class.getDeclaredField("type");
            field.setAccessible(true);
            return (Excel.Type) field.get(this);
        } catch (Exception e) {
            log.error("反射获取父类type属性异常");
            throw new ServiceException("Excel导出异常");
        }

    }

    public void setSubMethod(Method method) {
        try {
            Field field = ExcelUtil.class.getDeclaredField("subMethod");
            field.setAccessible(true);
            field.set(this, method);
        } catch (Exception e) {
            log.error("反射设置父类subMethod属性异常");
            throw new ServiceException("Excel导出异常");
        }
    }

    public void setSubFields(List<Field> fields) {
        try {
            Field field = ExcelUtil.class.getDeclaredField("subFields");
            field.setAccessible(true);
            field.set(this, fields);
        } catch (Exception e) {
            log.error("反射设置父类subFields属性异常");
            throw new ServiceException("Excel导出异常");
        }
    }
}
